﻿using Inovout;
using Inovout.Data.Domain;
using Inovout.Web.Mvc;
using KuaJing.Orders.Domain;
using KuaJing.Orders.Service;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.Mvc;

namespace KuaJing.Tasks.WebPages.Controllers
{
    public class HomeController : BaseController
    {
        public ActionResult Index(QueryCriteria queryCriteria)
        {
            return RedirectToAction("Daily");
        }

        public ActionResult Daily(DateTime? currentDate)
        {
            var orderService = GetService<OrderService>();
            currentDate = currentDate ?? orderService.Query().Max(o => o.ReportDate);
            var orderCountBarCharts = new Dictionary<DateTimeRangeKind, ChartInfo<int>>();
            var orderCountLineCharts = new Dictionary<DateTimeRangeKind, ChartInfo<DateTimeRange, int>>();
            var orderAmountBarCharts = new Dictionary<DateTimeRangeKind, ChartInfo<decimal>>();
            var orderAmountLineCharts = new Dictionary<DateTimeRangeKind, ChartInfo<DateTimeRange, decimal>>();
            foreach (var kind in new DateTimeRangeKind[] { DateTimeRangeKind.Day, DateTimeRangeKind.Week, DateTimeRangeKind.Month, DateTimeRangeKind.DoubleMonth, DateTimeRangeKind.Quarter })
            {
                var currentRange = new DateTimeRange(currentDate.Value, kind);
                var summaryOrderStatistics = GetOrderStatisticsSource(d => d.ReportDate <= currentRange.End, d => d.ReportDate.Value.Date);



                #region OrderCount
                orderCountLineCharts.Add(kind, BuildOrderLineChart(currentRange, new Dictionary<string, IEnumerable<KeyValuePair<DateTime, int>>>()
                {
                        { "总体订单数量", summaryOrderStatistics.Select(d => new KeyValuePair<DateTime, int>(d.Key, d.OrderCount))},
                    { "下单客户数量", summaryOrderStatistics.Select(d => new KeyValuePair<DateTime, int>(d.Key, d.CustomerCount))}

                }));

                orderCountBarCharts.Add(kind, BuildBarChart(new string[] { "江苏跨境", "南京贸互通" },
              GetCurrentRangeBarSource(currentRange, o => o.Provider,
              v => v.OrderCount, new Dictionary<string, Expression<Func<Order, bool>>>() {
                         {"总体", o => !o.IsInvalid},
                         {"新单", o => o.Customer.FirstOrderDate.Value >= currentRange.Start && o.Customer.FirstOrderDate.Value <= currentRange.End},
                         {"省内", o => o.Customer.Province == "江苏"},
                         {"省外", o => o.Customer.Province != "江苏"},
                         {"渠道", o => o.Customer.Source == CustomerSource.Channel || o.Customer.Source == CustomerSource.Introduction},
                         {"非渠道", o => o.Customer.Source != CustomerSource.Channel && o.Customer.Source != CustomerSource.Introduction},
                         {"营销中心", o => o.Customer.Sales.Organizations.Any(d => d.NamePath.StartsWith("/江苏跨境公司/营销中心"))},
                         {"渠道中心", o => o.Customer.Sales.Organizations.Any(d => d.NamePath.StartsWith("/江苏跨境公司/渠道中心")) }
                 })));
                #endregion

                #region OrderAmount
                orderAmountLineCharts.Add(kind, BuildOrderLineChart(currentRange, new KeyValuePair<string, IEnumerable<KeyValuePair<DateTime, decimal>>>[]
               {
                    new KeyValuePair<string, IEnumerable<KeyValuePair<DateTime, decimal>>>("总体订单金额", summaryOrderStatistics.Select(d => new KeyValuePair<DateTime, decimal>(d.Key, d.OrderAmount)))
            }));
                orderAmountBarCharts.Add(kind, BuildBarChart(new string[] { "江苏跨境", "南京贸互通" },
               GetCurrentRangeBarSource(currentRange, o => o.Provider,
               v => v.OrderAmount, new KeyValuePair<string, Expression<Func<Order, bool>>>[] {
               new KeyValuePair<string, Expression<Func<Order, bool>>>("总体", o => !o.IsInvalid),
               new KeyValuePair<string, Expression<Func<Order, bool>>>("省内", o => o.Customer.Province == "江苏"),
               new KeyValuePair<string, Expression<Func<Order, bool>>>("省外", o => o.Customer.Province != "江苏"),
               new KeyValuePair<string, Expression<Func<Order, bool>>>("广东", o => o.Customer.Province == "广东"),
               new KeyValuePair<string, Expression<Func<Order, bool>>>("渠道", o => o.Customer.Source == CustomerSource.Channel || o.Customer.Source == CustomerSource.Introduction),
               new KeyValuePair<string, Expression<Func<Order, bool>>>("非渠道", o => o.Customer.Source != CustomerSource.Channel && o.Customer.Source != CustomerSource.Introduction)
               })));
                #endregion
            }
            ViewBag.orderCountLineCharts = orderCountLineCharts;
            ViewBag.OrderCountBarCharts = orderCountBarCharts;
            ViewBag.OrderAmountLineCharts = orderAmountLineCharts;
            ViewBag.OrderAmountBarCharts = orderAmountBarCharts;
            ViewBag.CurrentDate = currentDate.Value.ToString("yyyy-M-d");
            var currentYearRange = new DateTimeRange(currentDate.Value, DateTimeRangeKind.Year);
            var currentYearSource = GetService<OrderService>().Query().Where(o => o.ReportDate >= currentYearRange.Start && o.ReportDate <= currentYearRange.End);
            ViewBag.CuurentYearChart = new OrderStatistics<DateTime>
            {
                Key = currentYearRange.Start,
                OrderCount = currentYearSource.Count(),
                OrderAmount = currentYearSource.Sum(o => o.TradeAmount),
                AvgAmount = currentYearSource.Average(o => o.TradeAmount),
                MinAmount = currentYearSource.Min(o => o.TradeAmount),
                MaxAmount = currentYearSource.Max(o => o.TradeAmount),
                CustomerCount = currentYearSource.Select(o => o.CustomerName).Distinct().Count()
            };
            return View("Index");

        }
        private IEnumerable<OrderStatistics<TKey>> GetOrderStatisticsSource<TKey>(Expression<Func<Order, bool>> whereFunc, Expression<Func<Order, TKey>> keySelector)
        {
            return GetOrderStatisticsSource(keySelector, whereFunc);
        }
        private IEnumerable<OrderStatistics<TKey>> GetOrderStatisticsSource<TKey>(Expression<Func<Order, TKey>> keySelector, params Expression<Func<Order, bool>>[] whereFuncs)
        {
            var orderQuery = GetService<OrderService>().Query().
                 Where(d => d.Customer != null && d.ReportDate != null);
            if (whereFuncs != null)
            {
                foreach (var whereFunc in whereFuncs)
                {
                    orderQuery = orderQuery.Where(whereFunc);
                }
            }
            return orderQuery.GroupBy(keySelector,
                 (key, os) => new OrderStatistics<TKey>
                 {
                     Key = key,
                     OrderCount = os.Count(),
                     OrderAmount = os.Sum(o => o.TradeAmount),
                     AvgAmount = os.Average(o => o.TradeAmount),
                     MinAmount = os.Min(o => o.TradeAmount),
                     MaxAmount = os.Max(o => o.TradeAmount),
                     CustomerCount = os.Select(o => o.CustomerName).Distinct().Count()
                 }).ToArray();

        }
        private KeyValuePair<string, IEnumerable<KeyValuePair<TKey, TValue>>>[] GetCurrentRangeBarSource<TKey, TValue>(DateTimeRange currentRange, Expression<Func<Order, TKey>> keySelector,
            Func<OrderStatistics<TKey>, TValue> ValueSelector, IEnumerable<KeyValuePair<string, Expression<Func<Order, bool>>>> whereFuncs)
        {
            List<KeyValuePair<string, IEnumerable<KeyValuePair<TKey, TValue>>>> result =
                new List<KeyValuePair<string, IEnumerable<KeyValuePair<TKey, TValue>>>>();
            foreach (var whereFunc in whereFuncs)
            {
                result.Add(new KeyValuePair<string, IEnumerable<KeyValuePair<TKey, TValue>>>(whereFunc.Key,
                 GetOrderStatisticsSource(keySelector, d => d.ReportDate >= currentRange.Start && d.ReportDate <= currentRange.End, whereFunc.Value)
                    .Select(d => new KeyValuePair<TKey, TValue>(d.Key, ValueSelector(d)))
                    ));
            }
            return result.ToArray();
        }
        private ChartInfo<TValue> BuildBarChart<TKey, TValue>(TKey[] keys, params KeyValuePair<string, IEnumerable<KeyValuePair<TKey, TValue>>>[] sources)
        {
            var chart = new ChartInfo<TValue>();

            chart.SetYCategories(sources.Select(kv => kv.Key));
            var series = new List<KeyValuePair<TKey, TValue>>();
            foreach (var source in sources)
            {
                for (int i = 0; i < keys.Length; i++)
                {
                    var key = keys[i];
                    var kv = source.Value.SingleOrDefault(k => k.Key.Equals(key));
                    if (kv.Equals(default(KeyValuePair<TKey, TValue>)))
                    {
                        kv = new KeyValuePair<TKey, TValue>(key, default(TValue));
                    }
                    if (i == 0)
                    {
                        kv = new KeyValuePair<TKey, TValue>(kv.Key,
                            GenericTypeConvert.Cast<decimal, TValue>(-1 * GenericTypeConvert.Cast<TValue, decimal>(kv.Value)));
                    }
                    series.Add(kv);
                }

            }
            foreach (var kv in series.GroupBy(s => s.Key))
            {
                chart.AddData(kv.Key.ToString(), kv.Select(s => s.Value));
            }
            return chart;
        }
        private ChartInfo<DateTimeRange, TValue> BuildOrderLineChart<TValue>(DateTimeRange currentRange,
          IEnumerable<KeyValuePair<string, IEnumerable<KeyValuePair<DateTime, TValue>>>> orderStatisticses)
        {
            var chart = new ChartInfo<DateTimeRange, TValue>();

            //先过滤（为今后时间查询作准备），分组（按年月日待）统计，只保留最近的10条，再按Value排序
            foreach (var orderStatistics in orderStatisticses)
            {
                var orderSource = orderStatistics.Value.GroupBy(d => new DateTimeRange(d.Key, currentRange.Kind), (key, list) =>
                    new KeyValuePair<DateTimeRange, TValue>(key,
                     GenericTypeConvert.Cast<decimal, TValue>(list.Sum(d => GenericTypeConvert.Cast<TValue, decimal>(d.Value)))))
                     .OrderByDescending(d => d.Key).Take(1 + GetTakeCount(currentRange.Kind));

                chart.AddData(orderStatistics.Key, orderSource
               .Take(GetTakeDisplayCount(currentRange.Kind)).OrderBy(d => d.Key));

                if (orderStatisticses.First().Key == orderStatistics.Key)
                {
                    var porerlotLines = orderSource.OrderBy(d => d.Value);
                    if (porerlotLines.Any())
                    {
                        chart.SetPlotLines(new KeyValuePair<DateTimeRange, TValue>[] {
                porerlotLines.Skip(1).Last(),
                new KeyValuePair<DateTimeRange, TValue>(currentRange,
                    GenericTypeConvert.Cast<decimal,TValue>(porerlotLines.Skip(1).Average(d=> GenericTypeConvert.Cast<TValue,decimal>(d.Value)))),
                porerlotLines.First()});
                    }
                }
            }

            return chart;
        }


        private int GetTakeCount(DateTimeRangeKind kind)
        {
            switch (kind)
            {
                case DateTimeRangeKind.Day:
                    return 30;
                case DateTimeRangeKind.Week:
                case DateTimeRangeKind.Month:
                case DateTimeRangeKind.DoubleMonth:
                default:
                    return 3;
            }
        }
        private int GetTakeDisplayCount(DateTimeRangeKind kind)
        {
            switch (kind)
            {
                case DateTimeRangeKind.Day:
                    return 10;
                case DateTimeRangeKind.Week:
                case DateTimeRangeKind.Month:
                    return 10;
                case DateTimeRangeKind.DoubleMonth:
                    return 10;
                default:
                    return 10;
            }
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!GetService<OrderService>().Query().Any())
            {
                filterContext.Result = RedirectToAction("Sync263", "Employee", new { ReturnUrl = Url.Action("Edit", "ExchangeRate") });
            }
            base.OnActionExecuting(filterContext);
        }
    }
}